{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "147e576c",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain-academy/blob/main/module-3/edit-state-human-feedback.ipynb) [![Open in LangChain Academy](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66e9eba12c7b7688aa3dbb5e_LCA-badge-green.svg)](https://academy.langchain.com/courses/take/intro-to-langgraph/lessons/58239520-lesson-3-editing-state-and-human-feedback)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b2f2448-21c3-4196-9e61-0b47e7d0048b",
   "metadata": {},
   "source": [
    "# Editing graph state\n",
    "\n",
    "## Review\n",
    "\n",
    "We discussed motivations for human-in-the-loop:\n",
    "\n",
    "(1) `Approval` - We can interrupt our agent, surface state to a user, and allow the user to accept an action\n",
    "\n",
    "(2) `Debugging` - We can rewind the graph to reproduce or avoid issues\n",
    "\n",
    "(3) `Editing` - You can modify the state \n",
    "\n",
    "We showed how breakpoints support user approval, but don't yet know how to modify our graph state once our graph is interrupted!\n",
    "\n",
    "## Goals\n",
    "\n",
    "Now, let's show how to directly edit the graph state and insert human feedback."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "95d26b8c-d958-4d21-9ca4-4636d3dfe45c",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph langchain_openai langgraph_sdk langgraph-prebuilt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d5948594",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os, getpass\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "65a8df1f-a76a-4803-a532-ea9802106ac8",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "## Editing state \n",
    "\n",
    "Previously, we introduced breakpoints.\n",
    "\n",
    "We used them to interrupt the graph and await user approval before executing the next node.\n",
    "\n",
    "But breakpoints are also [opportunities to modify the graph state](https://docs.langchain.com/oss/javascript/langgraph/use-time-travel#3-update-the-state).\n",
    "\n",
    "Let's set up our agent with a breakpoint before the `assistant` node."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "bcf24f05-ac2b-455e-846c-0c50ac86e1f4",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "def multiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply a and b.\n",
    "\n",
    "    Args:\n",
    "        a: first int\n",
    "        b: second int\n",
    "    \"\"\"\n",
    "    return a * b\n",
    "\n",
    "# This will be a tool\n",
    "def add(a: int, b: int) -> int:\n",
    "    \"\"\"Adds a and b.\n",
    "\n",
    "    Args:\n",
    "        a: first int\n",
    "        b: second int\n",
    "    \"\"\"\n",
    "    return a + b\n",
    "\n",
    "def divide(a: int, b: int) -> float:\n",
    "    \"\"\"Divide a by b.\n",
    "\n",
    "    Args:\n",
    "        a: first int\n",
    "        b: second int\n",
    "    \"\"\"\n",
    "    return a / b\n",
    "\n",
    "tools = [add, multiply, divide]\n",
    "llm = ChatOpenAI(model=\"gpt-4o\")\n",
    "llm_with_tools = llm.bind_tools(tools)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "5dfe84af-5c62-4c3f-8ed7-96b5261f0b7b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "from langgraph.graph import MessagesState\n",
    "from langgraph.graph import START, StateGraph\n",
    "from langgraph.prebuilt import tools_condition, ToolNode\n",
    "\n",
    "from langchain_core.messages import HumanMessage, SystemMessage\n",
    "\n",
    "# System message\n",
    "sys_msg = SystemMessage(content=\"You are a helpful assistant tasked with performing arithmetic on a set of inputs.\")\n",
    "\n",
    "# Node\n",
    "def assistant(state: MessagesState):\n",
    "   return {\"messages\": [llm_with_tools.invoke([sys_msg] + state[\"messages\"])]}\n",
    "\n",
    "# Graph\n",
    "builder = StateGraph(MessagesState)\n",
    "\n",
    "# Define nodes: these do the work\n",
    "builder.add_node(\"assistant\", assistant)\n",
    "builder.add_node(\"tools\", ToolNode(tools))\n",
    "\n",
    "# Define edges: these determine the control flow\n",
    "builder.add_edge(START, \"assistant\")\n",
    "builder.add_conditional_edges(\n",
    "    \"assistant\",\n",
    "    # If the latest message (result) from assistant is a tool call -> tools_condition routes to tools\n",
    "    # If the latest message (result) from assistant is a not a tool call -> tools_condition routes to END\n",
    "    tools_condition,\n",
    ")\n",
    "builder.add_edge(\"tools\", \"assistant\")\n",
    "\n",
    "memory = MemorySaver()\n",
    "graph = builder.compile(interrupt_before=[\"assistant\"], checkpointer=memory)\n",
    "\n",
    "# Show\n",
    "display(Image(graph.get_graph(xray=True).draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "92a47fd5-1f60-41dc-9206-698ed8ece530",
   "metadata": {},
   "source": [
    "Let's run!\n",
    "\n",
    "We can see the graph is interrupted before the chat model responds. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a2ce488d-00e4-492e-a62c-dd98702c313f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Multiply 2 and 3\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"messages\": \"Multiply 2 and 3\"}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"values\"):\n",
    "    event['messages'][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "4be478ef-bd60-4d32-8a05-5f56c93a8396",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "StateSnapshot(values={'messages': [HumanMessage(content='Multiply 2 and 3', id='e7edcaba-bfed-4113-a85b-25cc39d6b5a7')]}, next=('assistant',), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef6a412-5b2d-601a-8000-4af760ea1d0d'}}, metadata={'source': 'loop', 'writes': None, 'step': 0, 'parents': {}}, created_at='2024-09-03T22:09:10.966883+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef6a412-5b28-6ace-bfff-55d7a2c719ae'}}, tasks=(PregelTask(id='dbee122a-db69-51a7-b05b-a21fab160696', name='assistant', error=None, interrupts=(), state=None),))"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state = graph.get_state(thread)\n",
    "state"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36ef63a1-2ab8-416d-babf-d35054e294f0",
   "metadata": {},
   "source": [
    "Now, we can directly apply a state update.\n",
    "\n",
    "Remember, updates to the `messages` key will use the `add_messages` reducer:\n",
    " \n",
    "* If we want to over-write the existing message, we can supply the message `id`.\n",
    "* If we simply want to append to our list of messages, then we can pass a message without an `id` specified, as shown below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "9179cff1-e529-473a-9ce2-e23b932c2063",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'configurable': {'thread_id': '1',\n",
       "  'checkpoint_ns': '',\n",
       "  'checkpoint_id': '1ef6a414-f419-6182-8001-b9e899eca7e5'}}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.update_state(\n",
    "    thread,\n",
    "    {\"messages\": [HumanMessage(content=\"No, actually multiply 3 and 3!\")]},\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d77b8d6a-8c7b-4f7a-b723-121af25ac829",
   "metadata": {},
   "source": [
    "Let's have a look.\n",
    "\n",
    "We called `update_state` with a new message. \n",
    "\n",
    "The `add_messages` reducer appends it to our state key, `messages`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "141b6aab-ec6d-44f3-beb1-6c22ac5f2158",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Multiply 2 and 3\n",
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "No, actually multiply 3 and 3!\n"
     ]
    }
   ],
   "source": [
    "new_state = graph.get_state(thread).values\n",
    "for m in new_state['messages']:\n",
    "    m.pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4041959-cc3a-4168-8cf7-06d1711921d8",
   "metadata": {},
   "source": [
    "Now, let's proceed with our agent, simply by passing `None` and allowing it proceed from the current state.\n",
    "\n",
    "We emit the current and then proceed to execute the remaining nodes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "f166bed2-87c9-41ec-b235-0305721c2d6b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "No, actually multiply 3 and 3!\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  multiply (call_Mbu8MfA0krQh8rkZZALYiQMk)\n",
      " Call ID: call_Mbu8MfA0krQh8rkZZALYiQMk\n",
      "  Args:\n",
      "    a: 3\n",
      "    b: 3\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: multiply\n",
      "\n",
      "9\n"
     ]
    }
   ],
   "source": [
    "for event in graph.stream(None, thread, stream_mode=\"values\"):\n",
    "    event['messages'][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b18dc1ca",
   "metadata": {},
   "source": [
    "Now, we're back at the `assistant`, which has our `breakpoint`.\n",
    "\n",
    "We can again pass `None` to proceed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "f5952731-0170-4589-a399-ee787df35400",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: multiply\n",
      "\n",
      "9\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "3 multiplied by 3 equals 9.\n"
     ]
    }
   ],
   "source": [
    "for event in graph.stream(None, thread, stream_mode=\"values\"):\n",
    "    event['messages'][-1].pretty_print()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "bc22c3e9-b00c-4ead-b752-a682b45b3718",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "### Editing graph state in Studio\n",
    "\n",
    "**⚠️ Notice**\n",
    "\n",
    "Since filming these videos, we've updated Studio so that it can now be run locally and accessed through your browser. This is the preferred way to run Studio instead of using the Desktop App shown in the video. It is now called _LangSmith Studio_ instead of _LangGraph Studio_. Detailed setup instructions are available in the \"Getting Setup\" guide at the start of the course. You can find a description of Studio [here](https://docs.langchain.com/langsmith/studio), and specific details for local deployment [here](https://docs.langchain.com/langsmith/quick-start-studio#local-development-server).  \n",
    "To start the local development server, run the following command in your terminal in the `/studio` directory in this module:\n",
    "\n",
    "```\n",
    "langgraph dev\n",
    "```\n",
    "\n",
    "You should see the following output:\n",
    "```\n",
    "- 🚀 API: http://127.0.0.1:2024\n",
    "- 🎨 Studio UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024\n",
    "- 📚 API Docs: http://127.0.0.1:2024/docs\n",
    "```\n",
    "\n",
    "Open your browser and navigate to the **Studio UI** URL shown above.\n",
    "\n",
    "The LangGraph API [supports editing graph state](https://docs.langchain.com/langsmith/add-human-in-the-loop). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "020efeba-fa80-4839-81f9-9ce228f9844e",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "if 'google.colab' in str(get_ipython()):\n",
    "    raise Exception(\"Unfortunately LangGraph Studio is currently not supported on Google Colab\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "642aabab-f822-4917-9d66-3314ac5008fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "# This is the URL of the local development server\n",
    "from langgraph_sdk import get_client\n",
    "client = get_client(url=\"http://127.0.0.1:2024\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "be74cb09",
   "metadata": {},
   "source": [
    "Our agent is defined in `studio/agent.py`. \n",
    "\n",
    "If you look at the code, you'll see that it *does not* have a breakpoint! \n",
    " \n",
    "Of course, we can add it to `agent.py`, but one very nice feature of the API is that we can pass in a breakpoint!\n",
    "\n",
    "Here, we pass a `interrupt_before=[\"assistant\"]`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "1c352f9e-6a0f-4a94-a083-b85b0233efa9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Receiving new event of type: metadata...\n",
      "--------------------------------------------------\n",
      "Receiving new event of type: values...\n",
      "{'content': 'Multiply 2 and 3', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': '882dabe4-b877-4d71-bd09-c34cb97c4f46', 'example': False}\n",
      "--------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "initial_input = {\"messages\": \"Multiply 2 and 3\"}\n",
    "thread = await client.threads.create()\n",
    "async for chunk in client.runs.stream(\n",
    "    thread[\"thread_id\"],\n",
    "    \"agent\",\n",
    "    input=initial_input,\n",
    "    stream_mode=\"values\",\n",
    "    interrupt_before=[\"assistant\"],\n",
    "):\n",
    "    print(f\"Receiving new event of type: {chunk.event}...\")\n",
    "    messages = chunk.data.get('messages', [])\n",
    "    if messages:\n",
    "        print(messages[-1])\n",
    "    print(\"-\" * 50)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13065dd9-5f43-47d6-ac2a-9dc15c0c54e6",
   "metadata": {},
   "source": [
    "We can get the current state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "4da2c464-3e71-496a-badc-671aeee168b6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'values': {'messages': [{'content': 'Multiply 2 and 3',\n",
       "    'additional_kwargs': {},\n",
       "    'response_metadata': {},\n",
       "    'type': 'human',\n",
       "    'name': None,\n",
       "    'id': '882dabe4-b877-4d71-bd09-c34cb97c4f46',\n",
       "    'example': False}]},\n",
       " 'next': ['assistant'],\n",
       " 'tasks': [{'id': 'a71c0b80-a679-57cb-aa59-a1655b763480',\n",
       "   'name': 'assistant',\n",
       "   'error': None,\n",
       "   'interrupts': [],\n",
       "   'state': None}],\n",
       " 'metadata': {'step': 0,\n",
       "  'run_id': '1ef6a41c-ea63-663f-b3e8-4f001bf0bf53',\n",
       "  'source': 'loop',\n",
       "  'writes': None,\n",
       "  'parents': {},\n",
       "  'user_id': '',\n",
       "  'graph_id': 'agent',\n",
       "  'thread_id': 'a95ffa54-2435-4a47-a9da-e886369ca8ee',\n",
       "  'created_by': 'system',\n",
       "  'assistant_id': 'fe096781-5601-53d2-b2f6-0d3403f7e9ca'},\n",
       " 'created_at': '2024-09-03T22:13:54.466695+00:00',\n",
       " 'checkpoint_id': '1ef6a41c-ead7-637b-8000-8c6a7b98379e',\n",
       " 'parent_checkpoint_id': '1ef6a41c-ead3-637d-bfff-397ebdb4f2ea'}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "current_state = await client.threads.get_state(thread['thread_id'])\n",
    "current_state"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4527bbf1-0927-41a6-aeef-d15e32bbbdc3",
   "metadata": {},
   "source": [
    "We can look at the last message in state."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "801ae2d9-0551-46b8-aee2-82293cee4011",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'content': 'Multiply 2 and 3',\n",
       " 'additional_kwargs': {},\n",
       " 'response_metadata': {},\n",
       " 'type': 'human',\n",
       " 'name': None,\n",
       " 'id': '882dabe4-b877-4d71-bd09-c34cb97c4f46',\n",
       " 'example': False}"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "last_message = current_state['values']['messages'][-1]\n",
    "last_message"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0581ba8-db3d-474d-9042-b1c7f3461caf",
   "metadata": {},
   "source": [
    "We can edit it!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "86b12be7-7e4a-40d0-8521-dced7c393c71",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'content': 'No, actually multiply 3 and 3!',\n",
       " 'additional_kwargs': {},\n",
       " 'response_metadata': {},\n",
       " 'type': 'human',\n",
       " 'name': None,\n",
       " 'id': '882dabe4-b877-4d71-bd09-c34cb97c4f46',\n",
       " 'example': False}"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "last_message['content'] = \"No, actually multiply 3 and 3!\"\n",
    "last_message"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "f84f2c24-f281-4591-90e5-de3a5547c9da",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'content': 'No, actually multiply 3 and 3!',\n",
       " 'additional_kwargs': {},\n",
       " 'response_metadata': {},\n",
       " 'type': 'human',\n",
       " 'name': None,\n",
       " 'id': '882dabe4-b877-4d71-bd09-c34cb97c4f46',\n",
       " 'example': False}"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "last_message"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce7b4280-6ae7-4246-9c87-44e0daa6c654",
   "metadata": {},
   "source": [
    "Remember, as we said before, updates to the `messages` key will use the same `add_messages` reducer. \n",
    "\n",
    "If we want to over-write the existing message, then we can supply the message `id`.\n",
    "\n",
    "Here, we did that. We only modified the message `content`, as shown above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "84d33b6e-32ff-4eca-8114-345e508f3481",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'configurable': {'thread_id': 'a95ffa54-2435-4a47-a9da-e886369ca8ee',\n",
       "  'checkpoint_ns': '',\n",
       "  'checkpoint_id': '1ef6a41d-cc8e-6979-8001-8c7c283b636c'},\n",
       " 'checkpoint_id': '1ef6a41d-cc8e-6979-8001-8c7c283b636c'}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "await client.threads.update_state(thread['thread_id'], {\"messages\": last_message})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f07f0d1-7083-4827-babd-d3702eb59a37",
   "metadata": {},
   "source": [
    "Now, we resume by passing `None`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "ef18d12d-e0a6-487a-9f32-ad30e2634a20",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Receiving new event of type: metadata...\n",
      "--------------------------------------------------\n",
      "Receiving new event of type: values...\n",
      "{'content': 'No, actually multiply 3 and 3!', 'additional_kwargs': {'additional_kwargs': {}, 'response_metadata': {}, 'example': False}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': '882dabe4-b877-4d71-bd09-c34cb97c4f46', 'example': False}\n",
      "--------------------------------------------------\n",
      "Receiving new event of type: values...\n",
      "{'content': '', 'additional_kwargs': {'tool_calls': [{'index': 0, 'id': 'call_vi16je2EIikHuT7Aue2sd1qd', 'function': {'arguments': '{\"a\":3,\"b\":3}', 'name': 'multiply'}, 'type': 'function'}]}, 'response_metadata': {'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_157b3831f5'}, 'type': 'ai', 'name': None, 'id': 'run-775b42f7-0590-4c54-aaeb-78599b1f12d2', 'example': False, 'tool_calls': [{'name': 'multiply', 'args': {'a': 3, 'b': 3}, 'id': 'call_vi16je2EIikHuT7Aue2sd1qd', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': None}\n",
      "--------------------------------------------------\n",
      "Receiving new event of type: values...\n",
      "{'content': '9', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'multiply', 'id': '226bfbad-0cea-4900-80c5-761a62bd4bc1', 'tool_call_id': 'call_vi16je2EIikHuT7Aue2sd1qd', 'artifact': None, 'status': 'success'}\n",
      "--------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "async for chunk in client.runs.stream(\n",
    "    thread[\"thread_id\"],\n",
    "    assistant_id=\"agent\",\n",
    "    input=None,\n",
    "    stream_mode=\"values\",\n",
    "    interrupt_before=[\"assistant\"],\n",
    "):\n",
    "    print(f\"Receiving new event of type: {chunk.event}...\")\n",
    "    messages = chunk.data.get('messages', [])\n",
    "    if messages:\n",
    "        print(messages[-1])\n",
    "    print(\"-\" * 50)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6a82dd35-cbc8-486d-8e20-10d0c4d138d6",
   "metadata": {},
   "source": [
    "We get the result of the tool call as `9`, as expected."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "1d1bb3c7-dc26-4c32-b3df-865f41ef3c73",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Receiving new event of type: metadata...\n",
      "--------------------------------------------------\n",
      "Receiving new event of type: values...\n",
      "{'content': '9', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'multiply', 'id': '226bfbad-0cea-4900-80c5-761a62bd4bc1', 'tool_call_id': 'call_vi16je2EIikHuT7Aue2sd1qd', 'artifact': None, 'status': 'success'}\n",
      "--------------------------------------------------\n",
      "Receiving new event of type: values...\n",
      "{'content': 'The result of multiplying 3 by 3 is 9.', 'additional_kwargs': {}, 'response_metadata': {'finish_reason': 'stop', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_157b3831f5'}, 'type': 'ai', 'name': None, 'id': 'run-859bbf47-9f35-4e71-ae98-9d93ee49d16c', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}\n",
      "--------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "async for chunk in client.runs.stream(\n",
    "    thread[\"thread_id\"],\n",
    "    assistant_id=\"agent\",\n",
    "    input=None,\n",
    "    stream_mode=\"values\",\n",
    "    interrupt_before=[\"assistant\"],\n",
    "):\n",
    "    print(f\"Receiving new event of type: {chunk.event}...\")\n",
    "    messages = chunk.data.get('messages', [])\n",
    "    if messages:\n",
    "        print(messages[-1])\n",
    "    print(\"-\" * 50)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6914c5ca-27e4-421c-835a-9e4327dac12f",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "## Awaiting user input\n",
    "\n",
    "So, it's clear that we can edit our agent state after a breakpoint.\n",
    "\n",
    "Now, what if we want to allow for human feedback to perform this state update?\n",
    "\n",
    "We'll add a node that serves as a placeholder for human feedback within our agent.\n",
    "\n",
    "This `human_feedback` node allow the user to add feedback directly to state.\n",
    " \n",
    "We specify the breakpoint using `interrupt_before` our `human_feedback` node.\n",
    "\n",
    "We set up a checkpointer to save the state of the graph up until this node."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "e4b475ff-681f-4660-80dd-d6ade7bd48e3",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# System message\n",
    "sys_msg = SystemMessage(content=\"You are a helpful assistant tasked with performing arithmetic on a set of inputs.\")\n",
    "\n",
    "# no-op node that should be interrupted on\n",
    "def human_feedback(state: MessagesState):\n",
    "    pass\n",
    "\n",
    "# Assistant node\n",
    "def assistant(state: MessagesState):\n",
    "   return {\"messages\": [llm_with_tools.invoke([sys_msg] + state[\"messages\"])]}\n",
    "\n",
    "# Graph\n",
    "builder = StateGraph(MessagesState)\n",
    "\n",
    "# Define nodes: these do the work\n",
    "builder.add_node(\"assistant\", assistant)\n",
    "builder.add_node(\"tools\", ToolNode(tools))\n",
    "builder.add_node(\"human_feedback\", human_feedback)\n",
    "\n",
    "# Define edges: these determine the control flow\n",
    "builder.add_edge(START, \"human_feedback\")\n",
    "builder.add_edge(\"human_feedback\", \"assistant\")\n",
    "builder.add_conditional_edges(\n",
    "    \"assistant\",\n",
    "    # If the latest message (result) from assistant is a tool call -> tools_condition routes to tools\n",
    "    # If the latest message (result) from assistant is a not a tool call -> tools_condition routes to END\n",
    "    tools_condition,\n",
    ")\n",
    "builder.add_edge(\"tools\", \"human_feedback\")\n",
    "\n",
    "memory = MemorySaver()\n",
    "graph = builder.compile(interrupt_before=[\"human_feedback\"], checkpointer=memory)\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32d4ceb6-a224-4307-8196-3f53d367df5c",
   "metadata": {},
   "source": [
    "We will get feedback from the user.\n",
    "\n",
    "We use `.update_state` to update the state of the graph with the human response we get, as before.\n",
    "\n",
    "We use the `as_node=\"human_feedback\"` parameter to apply this state update as the specified node, `human_feedback`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "3fc7bcd6-660c-4a8a-ad8d-e6698dcf6201",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Multiply 2 and 3\n",
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "no, multiply 3 and 3\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  multiply (call_sewrDyCrAJBQQecusUoT6OJ6)\n",
      " Call ID: call_sewrDyCrAJBQQecusUoT6OJ6\n",
      "  Args:\n",
      "    a: 3\n",
      "    b: 3\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: multiply\n",
      "\n",
      "9\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"messages\": \"Multiply 2 and 3\"}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"5\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()\n",
    "    \n",
    "# Get user input\n",
    "user_input = input(\"Tell me how you want to update the state: \")\n",
    "\n",
    "# We now update the state as if we are the human_feedback node\n",
    "graph.update_state(thread, {\"messages\": user_input}, as_node=\"human_feedback\")\n",
    "\n",
    "# Continue the graph execution\n",
    "for event in graph.stream(None, thread, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "abf4cf5f-c0cb-4fdb-be6b-271ae4e967e2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: multiply\n",
      "\n",
      "9\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "The result of multiplying 3 and 3 is 9.\n"
     ]
    }
   ],
   "source": [
    "# Continue the graph execution\n",
    "for event in graph.stream(None, thread, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
